/*
Copyright 2007 Julian Asamer

This file is part of Fractalicious.

    Fractalicious is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Fractalicious is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Fractalicious.  If not, see <http://www.gnu.org/licenses/>.
*/

#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#import <GraphicsServices/GraphicsServices.h>
#import "FView.h"
#import "Vector.h"
#import "DrawingData.h"
#import "ColorChooser.h"

static DrawingData * sharedInstance;


@implementation DrawingData 

+ (DrawingData *) sharedInstance {
	if (sharedInstance==nil) 
		sharedInstance = [[DrawingData alloc] init];
		
	return sharedInstance;
}

-(id) init {
	[super init];
	dynColor = false;
	dynStroke = false;
	lines = [[NSMutableArray alloc] initWithCapacity: 5000];
	lastRecs = [[NSMutableIndexSet alloc] init];
	actualLength=0;
	return self;
}

@end

@implementation Drawable 
- (void)drawWithContext: (CGContextRef) context transform: (NSAffineTransform *) trans {
	NSLog(@"Subclass me!");
}
@end

@implementation DrawableFractal

- (void)setFGColor: (FRGBColor) fg dynamic: (BOOL) dyn_color dynamicColorFactor: (float)dyn_c_factor bGColor: (FRGBColor) bg stroke: (float) s dynamic: (BOOL) dyn_stroke onlyLast: (BOOL) only_last firstNot: (BOOL) first_not {
	fgColor = fg;
	bgColor = bg;
	stroke = s;
	dynStroke = dyn_stroke;
	dynColor = dyn_color;
	dynCFactor = dyn_c_factor;
	onlyLast = only_last;
	firstNot = first_not;
}

- (void)info {
	NSLog(@"Noo... this is baaad.");
}


@end

@implementation Drawable_1s1t

- (id)initWithPoints:(float*) points depth: (int) d {
	[super init];
	depth = d;
	data = points;
	return self;
}

-(void)release {
	free(data);
	[super release];
}

- (void)drawWithContext: (CGContextRef) context transform: (NSAffineTransform *) trans {
	NSAffineTransformStruct transs = [trans transformStruct];
	if (!dynColor) {
		CGContextSetStrokeColorWithColor(context, [ColorUtils colorWithFRGBColor: fgColor]);
		if (!dynStroke) {
			CGContextSetLineWidth(context, stroke);
			int i;
			for (i=(onlyLast ? depth-1 : (firstNot ? 1 : 0)); i < depth ; i++) {

				CGContextMoveToPoint(context, 
					data[i*4]*transs.m11 + data[i*4 + 1]*transs.m21 + transs.tX, 
					data[i*4]*transs.m12 + data[i*4 + 1]*transs.m22 + transs.tY
				);
				CGContextAddLineToPoint(context, 
					data[i*4 + 2]*transs.m11 + data[i*4 + 3]*transs.m21 + transs.tX, 
					data[i*4 + 2]*transs.m12 + data[i*4 + 3]*transs.m22 + transs.tY
				);
				CGContextStrokePath(context);
			}
			CGContextStrokePath(context);
	
		} else { //dynStroke
			int i;
			for (i=(onlyLast ? depth-1 : (firstNot ? 1 : 0)); i<depth; i++) {
				CGContextSetLineWidth(context, stroke*sqrt(pow(data[i*4]*transs.m11 + data[i*4 + 1]*transs.m21 - data[i*4 + 2]*transs.m11 - data[i*4 + 3]*transs.m21,2) +
						pow(data[i*4]*transs.m12 + data[i*4 + 1]*transs.m22 - data[i*4 + 2]*transs.m12 - data[i*4 + 3]*transs.m22,2))/200);
				CGContextMoveToPoint(context, 
					data[i*4]*transs.m11 + data[i*4 + 1]*transs.m21 + transs.tX, 
					data[i*4]*transs.m12 + data[i*4 + 1]*transs.m22 + transs.tY
				);
				CGContextAddLineToPoint(context, 
					data[i*4 + 2]*transs.m11 + data[i*4 + 3]*transs.m21 + transs.tX, 
					data[i*4 + 2]*transs.m12 + data[i*4 + 3]*transs.m22 + transs.tY
				);
				CGContextStrokePath(context);
				
			}
		}
	} else 	{
		FHSBColor col = [ColorUtils HSBFromRGB: fgColor];
		if (!dynStroke) {
			CGContextSetLineWidth(context, stroke);
			int i;
			for (i=(onlyLast ? depth-1 : (firstNot ? 1 : 0)); i < depth ; i++) {
    			CGContextSetStrokeColorWithColor(context, [ColorUtils colorWithFRGBColor: [ColorUtils RGBFromHSB: col]]);
				col.h = (col.h+dynCFactor);
				if (col.h>1) col.h-=1;
				
				CGContextMoveToPoint(context, 
					data[i*4]*transs.m11 + data[i*4 + 1]*transs.m21 + transs.tX, 
					data[i*4]*transs.m12 + data[i*4 + 1]*transs.m22 + transs.tY
				);
				CGContextAddLineToPoint(context, 
					data[i*4 + 2]*transs.m11 + data[i*4 + 3]*transs.m21 + transs.tX, 
					data[i*4 + 2]*transs.m12 + data[i*4 + 3]*transs.m22 + transs.tY
				);
				CGContextStrokePath(context);
			}
			CGContextStrokePath(context);
    
		} else { //dynStroke
			int i;
			for (i=(onlyLast ? depth-1 : (firstNot ? 1 : 0)); i<depth; i++) {
				CGContextSetStrokeColorWithColor(context, [ColorUtils colorWithFHSBColor: col]);
				col.h = (col.h+dynCFactor);
				if (col.h>1) col.h-=1;
				CGContextSetLineWidth(context, stroke*sqrt(pow(data[i*4]*transs.m11 + data[i*4 + 1]*transs.m21 - data[i*4 + 2]*transs.m11 - data[i*4 + 3]*transs.m21,2) +
						pow(data[i*4]*transs.m12 + data[i*4 + 1]*transs.m22 - data[i*4 + 2]*transs.m12 - data[i*4 + 3]*transs.m22,2))/200);
				CGContextMoveToPoint(context, 
					data[i*4]*transs.m11 + data[i*4 + 1]*transs.m21 + transs.tX, 
					data[i*4]*transs.m12 + data[i*4 + 1]*transs.m22 + transs.tY
				);
				CGContextAddLineToPoint(context, 
					data[i*4 + 2]*transs.m11 + data[i*4 + 3]*transs.m21 + transs.tX, 
					data[i*4 + 2]*transs.m12 + data[i*4 + 3]*transs.m22 + transs.tY
				);
				CGContextStrokePath(context);

			}
		}
	}
}


- (void)info {
	NSLog(@"Fine! I've got %i Points!", depth);
}

@end

@implementation Drawable_1sxt

- (id)initWithPoints:(float**) points depths: (int *)ds depthsdepth: (int)dd {
	[super init];
	depthsdepth = dd;
	depths = ds;
	data = points;
	rel = NO;
	return self;
}

-(void)release {
	int i;
	for (i=0; i<depthsdepth; i++) {
		free(data[i]);
	}
	free(data);
	free(depths);
	rel = YES;
	[super release];
}

- (void)drawWithContext: (CGContextRef) context transform: (NSAffineTransform *) trans {
	NSLog(@"Drawin'");
	if (rel) {
		NSLog(@"Was released, tried to draw though!");
		return;
	}
	NSAffineTransformStruct transs = [trans transformStruct];
	if (!dynColor) {
		CGContextSetStrokeColorWithColor(context, [ColorUtils colorWithFRGBColor: fgColor]);
		if (!dynStroke) {
			CGContextSetLineWidth(context, stroke);
			int i,j;
			
			for (i=(onlyLast ? depthsdepth-1 : (firstNot ? 1 : 0)); i < depthsdepth; i++) {
				for (j=0; j<depths[i]; j++) {
					CGContextMoveToPoint(context, 
						data[i][j*4]*transs.m11 + data[i][j*4 + 1]*transs.m21 + transs.tX, 
						data[i][j*4]*transs.m12 + data[i][j*4 + 1]*transs.m22 + transs.tY
					);
					CGContextAddLineToPoint(context, 
						data[i][j*4 + 2]*transs.m11 + data[i][j*4 + 3]*transs.m21 + transs.tX, 
						data[i][j*4 + 2]*transs.m12 + data[i][j*4 + 3]*transs.m22 + transs.tY
					);
				} 
			}

			CGContextStrokePath(context);
	
		} else { //dynStroke
			int i,j;
			
			for (i=(onlyLast ? depthsdepth-1 : (firstNot ? 1 : 0)); i < depthsdepth; i++) {
				for (j=0; j<depths[i]; j++) {
					CGContextSetLineWidth(context, stroke*sqrt(pow(data[i][j*4]*transs.m11 + data[i][j*4 + 1]*transs.m21 - data[i][j*4 + 2]*transs.m11 - data[i][j*4 + 3]*transs.m21,2)
					+pow(data[i][j*4]*transs.m12 + data[i][j*4 + 1]*transs.m22 - data[i][j*4 + 2]*transs.m12 - data[i][j*4 + 3]*transs.m22,2))/200);
					
					CGContextMoveToPoint(context, 
						data[i][j*4]*transs.m11 + data[i][j*4 + 1]*transs.m21 + transs.tX, 
						data[i][j*4]*transs.m12 + data[i][j*4 + 1]*transs.m22 + transs.tY
					);
					CGContextAddLineToPoint(context, 
						data[i][j*4 + 2]*transs.m11 + data[i][j*4 + 3]*transs.m21 + transs.tX, 
						data[i][j*4 + 2]*transs.m12 + data[i][j*4 + 3]*transs.m22 + transs.tY
					);
					CGContextStrokePath(context);
				} 
			}
		}
	} else 	{
		FHSBColor col = [ColorUtils HSBFromRGB: fgColor];
		
		if (!dynStroke) {
			CGContextSetLineWidth(context, stroke);
			int i,j;
    
			for (i=(onlyLast ? depthsdepth-1 : (firstNot ? 1 : 0)); i < depthsdepth; i++) {
				CGContextSetStrokeColorWithColor(context, [ColorUtils colorWithFRGBColor: [ColorUtils RGBFromHSB: col]]);
				col.h = (col.h+dynCFactor);
				if (col.h>1) col.h-=1;
				
				for (j=0; j<depths[i]; j++) {
					CGContextMoveToPoint(context, 
						data[i][j*4]*transs.m11 + data[i][j*4 + 1]*transs.m21 + transs.tX, 
						data[i][j*4]*transs.m12 + data[i][j*4 + 1]*transs.m22 + transs.tY
					);
					CGContextAddLineToPoint(context, 
						data[i][j*4 + 2]*transs.m11 + data[i][j*4 + 3]*transs.m21 + transs.tX, 
						data[i][j*4 + 2]*transs.m12 + data[i][j*4 + 3]*transs.m22 + transs.tY
					);
				}
				CGContextStrokePath(context);
				 
			}
		} else { //dynStroke
			int i,j;
    
			for (i=(onlyLast ? depthsdepth-1 : (firstNot ? 1 : 0)); i < depthsdepth; i++) {
				CGContextSetStrokeColorWithColor(context, [ColorUtils colorWithFRGBColor: [ColorUtils RGBFromHSB: col]]);
				col.h = (col.h+dynCFactor);
				if (col.h>1) col.h-=1;
				
				for (j=0; j<depths[i]; j++) {
					CGContextSetLineWidth(context, stroke*sqrt(pow(data[i][j*4]*transs.m11 + data[i][j*4 + 1]*transs.m21 - data[i][j*4 + 2]*transs.m11 - data[i][j*4 + 3]*transs.m21,2)
					+pow(data[i][j*4]*transs.m12 + data[i][j*4 + 1]*transs.m22 - data[i][j*4 + 2]*transs.m12 - data[i][j*4 + 3]*transs.m22,2))/200);
    
					CGContextMoveToPoint(context, 
						data[i][j*4]*transs.m11 + data[i][j*4 + 1]*transs.m21 + transs.tX, 
						data[i][j*4]*transs.m12 + data[i][j*4 + 1]*transs.m22 + transs.tY
					);
					CGContextAddLineToPoint(context, 
						data[i][j*4 + 2]*transs.m11 + data[i][j*4 + 3]*transs.m21 + transs.tX, 
						data[i][j*4 + 2]*transs.m12 + data[i][j*4 + 3]*transs.m22 + transs.tY
					);
					CGContextStrokePath(context);
				} 
			}
		}
	}
}


- (void)info {
	NSLog(@"Fine! I've got %i Pointarrays!", depthsdepth);
}

@end

@implementation Placeholder 

- (id)init {
	[super init];
	return self;
}

- (void)drawWithContext: (CGContextRef) context transform: (NSAffineTransform *) trans {
	NSLog(@"Holding the position!");
}

- (void)info {
	NSLog(@"I'm just a placeholder.");
}
@end